﻿using System;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Generic;
using System.Linq;
using System.Configuration;

namespace ManageMoney.DataAccess
{
    /// <summary>
    /// Bases de datos usadas por el sistema.
    /// </summary>
    public enum TypeDataBase
    {
        /// <summary>
        /// Base de datos de Prueba
        /// </summary>
        Prueba,

        /// <summary>
        /// Base de datos del Generador de Codigo
        /// </summary>
        GeneradorCodigo,

        ManageMoney
    }

    /// <summary>
    /// Clase base para acceso a datos con gestión de auditoría
    /// </summary>
    public class DataUtil : IDisposable
    {
        #region VARIABLES
        /// <summary>
        /// Conexión a base de datos
        /// </summary>
        private readonly SqlConnection _conexion;

        /// <summary>
        /// Comando usado realizar consultas a la base de datos
        /// </summary>
        private SqlCommand _command;

        /// <summary>
        /// Adaptador para cargar los datasets
        /// </summary>
        private readonly SqlDataAdapter _adapter;

        /// <summary>
        /// Adaptador para cargar los datasets
        /// </summary>
        private Dictionary<string, int> _indexReader;

        #endregion

        #region PROPIEDADES
        /// <summary>
        /// Comando usado realizar consultas a la base de datos.
        /// </summary>
        public SqlCommand Comando
        {
            get
            {
                return _command;
            }
            set
            {
                _command = value;
            }
        }

        #endregion

        #region CONSTRUCTORES Y DESTRUCTORES

        /// <summary>
        /// Constructor de la clase, usado para cargar DataSets, DataTablas, Execute, Scalar
        /// </summary>        
        /// <param name="typeDataBase">Cadena de conexión a la base de datos</param>
        public DataUtil(string cadenaDataBase)
        {
            _conexion = new SqlConnection(cadenaDataBase);

            _command = new SqlCommand
            {
                Connection = _conexion
            };

            _adapter = new SqlDataAdapter(_command);
        }

        /// <summary>
        /// Constructor de la clase, usado para cargar DataSets, DataTablas, Execute, Scalar
        /// </summary>        
        /// <param name="typeDataBase">Cadena de conexión a la base de datos</param>
        /// <param name="commandType">Tipo de comando sea Store Procedure o sql</param>
        public DataUtil(string cadenaDataBase, CommandType commandType)
        {
            _conexion = new SqlConnection(cadenaDataBase);

            _command = new SqlCommand
            {
                CommandType = commandType,
                Connection = _conexion
            };

            _adapter = new SqlDataAdapter(_command);
        }

        /// <summary>
        /// Constructor de la clase, usado para trabajar data readers
        /// </summary>        
        /// <param name="typeDataBase">Cadena de conexión a la base de datos</param>
        public DataUtil(string cadenaDataBase, CommandType commandType, string commandText)
        {
            _conexion = new SqlConnection(cadenaDataBase);

            _command = new SqlCommand
            {
                CommandType = commandType,
                CommandText = commandText,
                Connection = _conexion
            };

            _adapter = new SqlDataAdapter(_command);
        }

        /// <summary>
        /// Constructor de la clase, usado para trabajar data readers
        /// </summary>        
        /// <param name="typeDataBase">Cadena de conexión a la base de datos</param>
        public DataUtil(string cadenaDataBase, CommandType commandType, string commandText, Dictionary<string, object> parameters)
        {
            _conexion = new SqlConnection(cadenaDataBase);

            _command = new SqlCommand
            {
                CommandType = commandType,
                CommandText = commandText,
                Connection = _conexion
            };

            _adapter = new SqlDataAdapter(_command);

            _indexReader = new Dictionary<string, int>();

            _command.Connection.Open();

            this.AddParametersDictionary(parameters);
        }

        /// <summary>
        /// Constructor de la clase, usado para trabajar data readers
        /// </summary>        
        /// <param name="typeDataBase">Cadena de conexión a la base de datos</param>
        public DataUtil(TypeDataBase typeDataBase, CommandType commandType, string commandText, Dictionary<string, object> parameters)
        {
            _conexion = new SqlConnection(this._getConecction(typeDataBase));

            _command = new SqlCommand
            {
                CommandType = commandType,
                CommandText = commandText,
                Connection = _conexion
            };

            _adapter = new SqlDataAdapter(_command);

            _indexReader = new Dictionary<string, int>();

            _command.Connection.Open();

            this.AddParametersDictionary(parameters);
        }

        /// <summary>
        /// Constructor de la clase, usado para cargar DataSets, DataTablas, Execute, Scalar
        /// </summary>        
        /// <param name="typeDataBase">Cadena de conexión a la base de datos</param>
        public DataUtil(TypeDataBase typeDataBase)
        {
            _conexion = new SqlConnection(this._getConecction(typeDataBase));

            _command = new SqlCommand
            {
                Connection = _conexion
            };

            _adapter = new SqlDataAdapter(_command);
        }

        /// <summary>
        /// Constructor de la clase, usado para cargar DataSets, DataTablas, Execute, Scalar
        /// </summary>        
        /// <param name="typeDataBase">Cadena de conexión a la base de datos</param>
        /// <param name="commandType">Tipo de comando sea Store Procedure o sql</param>
        public DataUtil(TypeDataBase typeDataBase,CommandType commandType)
        {
            _conexion = new SqlConnection(this._getConecction(typeDataBase));

            _command = new SqlCommand
            {
                CommandType = commandType,
                Connection = _conexion
            };

            _adapter = new SqlDataAdapter(_command);
        }

        /// <summary>
        /// Constructor de la clase, usado para trabajar data readers
        /// </summary>        
        /// <param name="typeDataBase">Cadena de conexión a la base de datos</param>
        public DataUtil(TypeDataBase typeDataBase, CommandType commandType,string commandText)
        {
            _conexion = new SqlConnection(this._getConecction(typeDataBase));

            _command = new SqlCommand
            {
                CommandType = commandType,
                CommandText = commandText,
                Connection = _conexion
            };

            _adapter = new SqlDataAdapter(_command);

            _indexReader = new Dictionary<string, int>();

            _command.Connection.Open();
        }

        /// <summary>
        /// Destructor de la instancia
        /// </summary>
        ~DataUtil()
        {
            Dispose(false);
        }

        #endregion

        #region MÉTODOS PARA PARAMETROS SQL

        /// <summary>
        /// Inicializa los objetos necesarios para la conexión a la base de datos
        /// </summary>    
        /// <param name="parameters">Parámetros del procedimiento almacenado</param>
        public void InitializeParameters(params SqlParameter[] parameters)
        {
            foreach (SqlParameter item in parameters)
            {
                _command.Parameters.Add(item);
            }
        }

        /// <summary>
        /// Agrega un parámetro y le asigna un valor
        /// </summary>
        /// <param name="parameterName">Nombre del parámetro</param>
        /// <param name="value">Valor del parámetro</param>
        /// <param name="isPK">Indica si el parámetro actuá como llave primaria en la tabla</param>
        public void AddParameter(string parameterName, object value, bool isPK = false)
        {
            this.Comando.Parameters.AddWithValue(parameterName, value ?? DBNull.Value);
        }

        /// <summary>
        /// Devuelve el valor de un parametro
        /// </summary>
        /// <param name="parameterName">Nombre del parámetro</param>
        /// <returns>Valor del parametro</returns>
        public object GetValueParameter(string parameterName)
        {
            return Comando.Parameters[parameterName].Value;
        }

        /// <summary>
        /// Agrega un parámetro y le asigna un valor
        /// </summary>
        /// <param name="parameterName">Nombre del parámetro</param>
        /// <param name="value">Valor del parámetro</param>
        /// <param name="direction">Dirección del parámetro</param>
        /// <param name="sqlDbType">Tipo de datos del parámetro</param>
        public void AddParameter(string parameterName, object value, ParameterDirection direction, SqlDbType sqlDbType)
        {
            AddParameter(parameterName, value);
            Comando.Parameters[parameterName].Direction = direction;
            Comando.Parameters[parameterName].SqlDbType = sqlDbType;
        }

        /// <summary>
        /// Agrega un parámetro y le asigna un valor
        /// </summary>
        /// <param name="parameterName">Nombre del parámetro</param>
        /// <param name="value">Valor del parámetro</param>
        /// <param name="sqlDbType">Tipo de datos del parámetro</param>
        public void AddParameter(string parameterName, object value, SqlDbType sqlDbType)
        {
            AddParameter(parameterName, value);
            Comando.Parameters[parameterName].Direction = ParameterDirection.Input;
            Comando.Parameters[parameterName].SqlDbType = sqlDbType;
        }

        /// <summary>
        /// Agrega un parámetro y le asigna un valor
        /// </summary>
        /// <param name="parameterName">Nombre del parámetro</param>
        /// <param name="value">Valor del parámetro</param>
        /// <param name="direction">Dirección del parámetro</param>
        /// <param name="isPK">Es LLave Primaria</param>
        public void AddParameter(string parameterName, object value, ParameterDirection direction, bool isPK = false)
        {
            AddParameter(parameterName, value, isPK);
            Comando.Parameters[parameterName].Direction = direction;
        }

        /// <summary>
        /// Agrega un parámetro y le asigna un valor
        /// </summary>
        /// <param name="parameterName">Nombre del parámetro</param>
        /// <param name="value">Valor del parámetro</param>
        /// <param name="type">Tipo de datos del parámetro</param>      
        /// <param name="size">Tamaño del parámetro</param>    
        /// <param name="isPK">Es LLave Primaria</param>
        public void AddParameter(string parameterName, object value, SqlDbType type, int size, bool isPK = false)
        {
            AddParameter(parameterName, value, isPK);

            Comando.Parameters[parameterName].SqlDbType = type;
            Comando.Parameters[parameterName].Size = size;
        }

        /// <summary>
        /// Agrega un parámetro y le asigna un valor
        /// </summary>
        /// <param name="parameterName">Nombre del parámetro</param>
        /// <param name="value">Valor del parámetro</param>
        /// <param name="direction">Dirección del parámetro</param>
        /// <param name="type">Tipo de datos del parámetro</param>
        /// <param name="size">Tamaño del parámetro</param>       
        /// <param name="isPK">Es LLave Primaria</param> 
        public void AddParameter(string parameterName, object value, ParameterDirection direction, SqlDbType type, int size, bool isPK = false)
        {
            AddParameter(parameterName, value, isPK);

            Comando.Parameters[parameterName].Direction = direction;
            Comando.Parameters[parameterName].SqlDbType = type;
            Comando.Parameters[parameterName].Size = size;
        }

        /// <summary>
        /// Crea un parámetro para un comando
        /// </summary>
        /// <param name="parameter">Nombre del parámetro</param>
        /// <param name="value">Valor del parámetro</param>
        /// <param name="type">Tipo de datos del parámetro</param>
        /// <returns>Parámetro para seu usado por un comando</returns>
        public static SqlParameter CreateParameter(string parameter, object value, SqlDbType type)
        {
            var objParameter = new SqlParameter
            {
                ParameterName = parameter,
                Direction = ParameterDirection.Input,
                SqlDbType = type,
                Value = value ?? DBNull.Value
            };


            return objParameter;
        }

        /// <summary>
        /// Crea un parámetro para un comando
        /// </summary>
        /// <param name="parameter">Nombre del parámetro</param>
        /// <param name="value">Valor del parámetro</param>
        /// <param name="direction">Dirección del parámetro</param>
        /// <param name="type">Tipo de datos del parámetro</param>
        /// <returns>Parámetro para seu usado por un comando</returns>
        public static SqlParameter CreateParameter(string parameter, object value, ParameterDirection direction, SqlDbType type)
        {
            var objParameter = new SqlParameter
            {
                ParameterName = parameter,
                Direction = direction,
                SqlDbType = type,
                Value = value ?? DBNull.Value
            };

            return objParameter;
        }

        /// <summary>
        /// Crea un parámetro para un comando
        /// </summary>
        /// <param name="parameter">Nombre del parámetro</param>
        /// <param name="value">Valor del parámetro</param>
        /// <param name="direction">Dirección del parámetro</param>        
        /// <returns>Parámetro para seu usado por un comando</returns>
        public static SqlParameter CreateParameter(string parameter, object value, ParameterDirection direction)
        {
            var objParameter = new SqlParameter { ParameterName = parameter, Direction = direction, Value = value ?? DBNull.Value };

            return objParameter;
        }

        /// <summary>
        /// Crea un parámetro para un comando
        /// </summary>
        /// <param name="parameter">Nombre del parámetro</param>
        /// <param name="value">Valor del parámetro</param>
        /// <param name="type">Tipo de datos del parámetro</param>
        /// <param name="size">Tamaño del parámetro</param>
        /// <returns>Parámetro para seu usado por un comando</returns>
        public static SqlParameter CreateParameter(string parameter, object value, SqlDbType type, int size)
        {
            var objParameter = new SqlParameter
            {
                ParameterName = parameter,
                Direction = ParameterDirection.Input,
                SqlDbType = type,
                Size = size,
                Value = value ?? DBNull.Value
            };

            return objParameter;
        }

        /// <summary>
        /// Crea un parámetro para un comando
        /// </summary>
        /// <param name="parameter">Nombre del parámetro</param>
        /// <param name="value">Valor del parámetro</param>
        /// <param name="direction">Dirección del parámetro</param>
        /// <param name="type">Tipo de datos del parámetro</param>
        /// <param name="size">Tamaño del parámetro</param>
        /// <returns>Parámetro para seu usado por un comando</returns>
        public static SqlParameter CreateParameter(string parameter, object value, ParameterDirection direction, SqlDbType type, int size)
        {
            var objParameter = new SqlParameter
            {
                ParameterName = parameter,
                Direction = direction,
                SqlDbType = type,
                Size = size,
                Value = value ?? DBNull.Value
            };

            return objParameter;
        }

        /// <summary>
        /// Crea un parámetro para un comando
        /// </summary>
        /// <param name="parameter">Nombre del parámetro</param>
        /// <param name="value">Valor del parámetro</param>
        /// <param name="type">Tipo de datos del parámetro</param>
        /// <param name="precision">Precisión del tipo de datos</param>
        /// <param name="scale">Escala del tipo de datos</param>
        /// <returns>Parámetro para seu usado por un comando</returns>
        public static SqlParameter CreateParameter(string parameter, object value, SqlDbType type, byte precision, byte scale)
        {
            var objParameter = new SqlParameter
            {
                ParameterName = parameter,
                Direction = ParameterDirection.Input,
                SqlDbType = type,
                Precision = precision,
                Scale = scale,
                Value = value ?? DBNull.Value
            };

            return objParameter;
        }

        /// <summary>
        /// Crea un parámetro para un comando
        /// </summary>
        /// <param name="parameter">Nombre del parámetro</param>
        /// <param name="value">Valor del parámetro</param>
        /// <param name="direction">Dirección del parámetro</param>
        /// <param name="type">Tipo de datos del parámetro</param>
        /// <param name="precision">Precisión del tipo de datos</param>
        /// <param name="scale">Escala del tipo de datos</param>
        /// <returns>Parámetro para seu usado por un comando</returns>
        public static SqlParameter CreateParameter(string parameter, object value, ParameterDirection direction, SqlDbType type, byte precision, byte scale)
        {
            var objParameter = new SqlParameter
            {
                ParameterName = parameter,
                Direction = direction,
                SqlDbType = type,
                Precision = precision,
                Scale = scale,
                Value = value ?? DBNull.Value
            };

            return objParameter;
        }

        #endregion

        #region MÉTODOS PARA LECTURA Y ESCRITURA EN LA BASE DE DATOS

        /// <summary>
        /// Obtiene la cadena de conexión a base de datos
        /// </summary>
        /// <param name="typeBD">Base de datos</param>
        /// <returns>Cadena de conexión</returns>
        private string _getConecction(TypeDataBase typeBD)
        {
            string connectionStrings = "";

            switch (typeBD)
            {
                case TypeDataBase.Prueba:
                    connectionStrings = "baseDatosPrueba";
                    break;
                case TypeDataBase.GeneradorCodigo:
                    connectionStrings = "baseDatosGeneradorCodigo";
                    break;
                case TypeDataBase.ManageMoney:
                    connectionStrings = "BDManageMoney";
                    break;
            }

            ConfigurationManager.RefreshSection("connectionStrings");

            return ConfigurationManager.ConnectionStrings[connectionStrings].ConnectionString;
        }

        /// <summary>
        /// Guarda en memoria 
        /// </summary>
        /// <param name="enuCommandBehavior">Acción que se realiza con la conexión a base de datos</param>
        /// <returns>Retorna el conjunto de resultados</returns>
        private void _memoryIndexReader(IDataReader reader)
        {
            if (reader == null) return;

            using(DataTable t = reader.GetSchemaTable())
            {
                foreach (DataRow r in t.Rows)
                    _indexReader.Add(r[0].ToString().Trim().ToUpper(),Convert.ToInt16(r[1]));
            }
        }

        /// <summary>
        /// Coloca en una sola transaccion todo el SQL
        /// </summary>
        /// <param name="enuCommandBehavior">Acción que se realiza con la conexión a base de datos</param>
        /// <returns>Retorna el conjunto de resultados</returns>
        private void _sqlBeginTransactionDataBase()
        {
            if (this.Comando.CommandType != CommandType.Text) return;

            this.Comando.CommandText = string.Format(@"BEGIN TRY 
	                                                            BEGIN TRANSACTION 
                                                                    {0}
                                                                COMMIT TRANSACTION 
                                                            END TRY
                                                            BEGIN CATCH 
                                                                DECLARE @ErrorMessage NVARCHAR(4000);
                                                                DECLARE @ErrorSeverity INT;
                                                                DECLARE @ErrorState INT;
                                                                SELECT 
                                                                        @ErrorMessage = ERROR_MESSAGE() ,
                                                                        @ErrorSeverity = ERROR_SEVERITY(),
                                                                        @ErrorState = ERROR_STATE();

                                                                ROLLBACK TRANSACTION;
                                                                RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState);
                                                            END CATCH", this.Comando.CommandText);
        }

        /// <summary>
        /// Mensaje de error SQL
        /// </summary>
        /// <param name="enuCommandBehavior">Acción que se realiza con la conexión a base de datos</param>
        /// <returns>Retorna el conjunto de resultados</returns>
        private void _messageError(SqlException errm)
        {
            if (_conexion.State == ConnectionState.Open) _conexion.Close();

            //Catch error message
            string mssgs = String.Format("{0}.  No.:{1:#####} {2}  SQL:{3}", errm.Source, errm.Number, errm.Message, this.Comando.CommandText);

            throw new ApplicationException(mssgs);

            //if (errm.Number == 1205)
            //{
            //    if (i >= _countInterbloqueo) throw new ApplicationException(mssgs);

            //    //Wait Random Time to Repeat
            //    System.Threading.Thread.Sleep(_random.Next(1, 30 * 2) * (1000 / 2));
            //}
            //else
            //{
            //    throw new ApplicationException(mssgs);

            //    return false;
            //}
        }

        /// <summary>
        /// Busca el ordinal del reader segun el nombre de la columna
        /// </summary>
        /// <param name="enuCommandBehavior">Acción que se realiza con la conexión a base de datos</param>
        /// <returns>Retorna el conjunto de resultados</returns>
        public int GetOrdinalReader(string columnName)
        {
            columnName = columnName.ToUpper();

            var index = (from entry in _indexReader where entry.Key == columnName select entry.Value);

            if (!index.Any()) return -1;

            return index.ToList()[0];
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="commandText"></param>
        public DataTable DataTable(string commandText)
        {
            try
            {
                using (DataTable dataTable = new DataTable())
                {
                    _command.CommandText = commandText;
                    _adapter.Fill(dataTable);
                    _command.Parameters.Clear();

                    return dataTable;
                }
            }
            catch(SqlException errm)
            {
                this._messageError(errm);

                return new DataTable();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="commandText"></param>
        public DataSet DataSet(string commandText)
        {
            try
            {
                using (DataSet dataSet = new DataSet())
                {
                    _command.CommandText = commandText;
                    _adapter.Fill(dataSet);
                    _command.Parameters.Clear();

                    return dataSet;
                }
            }
            catch (SqlException errm)
            {
                this._messageError(errm);

                return new DataSet();
            }
        }

        /// <summary>
        /// Ejecuta el comando
        /// </summary>
        /// <param name="enuCommandBehavior">Acción que se realiza con la conexión a base de datos</param>
        /// <returns>Retorna el conjunto de resultados</returns>
        public IDataReader ExecuteReader(CommandBehavior enuCommandBehavior = CommandBehavior.CloseConnection)
        {
            IDataReader r = null;

            try
            {
                r = this.Comando.ExecuteReader(enuCommandBehavior);

                this._memoryIndexReader(r);

                return r;
            }
            catch (SqlException errm)
            {
                this._messageError(errm);

                return r;
            }
        }

        /// <summary>
        /// Ejecuta el comando
        /// </summary>
        /// <returns>Cantidad de filas afectadas</returns>
        public int ExecuteNonQuery(bool beginTransaction = true)
        {
            try
            {
                if (_conexion.State == ConnectionState.Closed) _conexion.Open();

                if (beginTransaction) this._sqlBeginTransactionDataBase();

                int intNumRows = this.Comando.ExecuteNonQuery();

                if (_conexion.State == ConnectionState.Open) _conexion.Close();

                return intNumRows;
            }
            catch (SqlException errm)
            {
                this._messageError(errm);

                return 0;
            }
        }

        /// <summary>
        /// Ejecuta  scalar
        /// </summary>
        /// <returns>Cantidad de filas afectadas</returns>
        public object ExecuteScalar(bool beginTransaction = false)
        {
            try
            {
                if (_conexion.State == ConnectionState.Closed) _conexion.Open();

                if (beginTransaction) this._sqlBeginTransactionDataBase();

                object obj = Comando.ExecuteScalar();

                if (_conexion.State == ConnectionState.Open) _conexion.Close();

                return obj;
            }
            catch (SqlException errm)
            {
                this._messageError(errm);

                return null;
            }
        }

        /// <summary>
        /// Ejecuta  scalar
        /// </summary>
        /// <returns>Cantidad de filas afectadas</returns>
        public void AddParametersDictionary(Dictionary<string,object> parameters)
        {
            foreach (KeyValuePair<string, object> p in parameters)
                this.AddParameter(p.Key, p.Value);
        }

        
        #endregion

        #region IDisposable
        /// <summary>
        /// Indica si ya se llamo al método Dispose. (default = false)
        /// </summary>
        private Boolean _disposed;

        /// <summary>
        /// Libera los recursos usados
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Inicia la liberación de recursos usados
        /// </summary>
        /// <param name="disposing">Indica si se inicia la liberación de recursos</param>
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed)
            {
                if (disposing)
                {
                    _command.Dispose();
                    _conexion.Dispose();
                }
            }

            _disposed = true;
        }

        #endregion
    }

    /// <summary>
    /// Apoyo para gestionar DataReader
    /// </summary>
    public static class DataRecordExtensions
    {
        /// <summary>
        /// Verifica si un DataReader contiene una columna
        /// </summary>
        /// <param name="dr"></param>
        /// <param name="columnName"></param>
        /// <returns></returns>
        public static bool HasColumn(this IDataRecord dr, string columnName)
        {
            for (int i = 0; i < dr.FieldCount; i++)
            {
                if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
                    return true;
            }
            return false;
        }
    }

    /// <summary>
    /// Conversion de Tipos de Datos
    /// </summary>
    public static class DataValue
    {
        /// <summary>
        /// convertir a string
        /// </summary>
        /// <param name="paramValue"></param>
        /// <returns></returns>
        public static string ToString(object paramValue)
        {
            if (paramValue != null && paramValue != DBNull.Value)
                if (paramValue.ToString() != "")
                    return paramValue.ToString();

            return null;
        }

        /// <summary>
        /// convertir a integer
        /// </summary>
        /// <param name="paramValue"></param>
        /// <returns></returns>
        public static int? ToInt(object paramValue)
        {
            if (paramValue != null && paramValue != DBNull.Value)
                if (paramValue.ToString() != "")
                    return (int)paramValue;

            return null;
        }

        /// <summary>
        /// convertir a double
        /// </summary>
        /// <param name="paramValue"></param>
        /// <returns></returns>
        public static double? ToDouble(object paramValue)
        {
            if (paramValue != null && paramValue != DBNull.Value)
                if (paramValue.ToString() != "")
                    return (double)paramValue;

            return null;
        }

        /// <summary>
        /// convertir a datetime
        /// </summary>
        /// <param name="paramValue"></param>
        /// <returns></returns>
        public static DateTime? ToDateTime(object paramValue)
        {
            if (paramValue != null && paramValue != DBNull.Value)
                if (paramValue.ToString() != "")
                    return (DateTime)paramValue;

            return null;
        }

        /// <summary>
        /// convertir a time
        /// </summary>
        /// <param name="paramValue"></param>
        /// <returns></returns>
        public static TimeSpan? ToTime(object paramValue)
        {
            if (paramValue != null && paramValue != DBNull.Value)
                if (paramValue.ToString() != "")
                    return Convert.ToDateTime(paramValue).TimeOfDay;

            return null;
        }

        /// <summary>
        /// convertir a boolean
        /// </summary>
        /// <param name="paramValue"></param>
        /// <returns></returns>
        public static bool? ToBoolean(object paramValue)
        {
            if (paramValue != null && paramValue != DBNull.Value)
                if (paramValue.ToString() != "")
                    return (bool)paramValue;

            return null;
        }

        /// <summary>
        /// Si el valor es nulo, devuelve el segundo, sino devuelve el primero
        /// </summary>
        /// <param name="paramValue"></param>
        /// <param name="paramNull"></param>
        /// <returns></returns>
        public static object NullCoalescing(object paramValue, object paramNull)
        {
            return paramValue ?? paramNull;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    public static class DataReader
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="reader"></param>
        /// <param name="i"></param>
        /// <returns></returns>
        public static string GetStringDefault(this System.Data.IDataReader reader, int i)
        {
            if (reader.IsDBNull(i))
                return string.Empty;
            return reader.GetString(i);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataReader"></param>
        /// <param name="fieldName"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static string GetStringDefault(this IDataReader dataReader, string fieldName, string defaultValue = "")
        {
            var fieldOrdinal = dataReader.GetOrdinal(fieldName);
            return dataReader.IsDBNull(fieldOrdinal) ? defaultValue : dataReader.GetString(fieldOrdinal);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataReader"></param>
        /// <param name="fieldName"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static Decimal GetDecimalDefault(this IDataReader dataReader, string fieldName, Decimal defaultValue = 0m)
        {
            var fieldOrdinal = dataReader.GetOrdinal(fieldName);
            return dataReader.IsDBNull(fieldOrdinal) ? defaultValue : dataReader.GetDecimal(fieldOrdinal);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataReader"></param>
        /// <param name="fieldName"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static Int32 GetInt32Default(this System.Data.IDataReader dataReader, string fieldName, Int32 defaultValue = 0)
        {
            var fieldOrdinal = dataReader.GetOrdinal(fieldName);
            return dataReader.IsDBNull(fieldOrdinal) ? defaultValue : dataReader.GetInt32(fieldOrdinal);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="dataReader"></param>
        /// <param name="fieldName"></param>
        /// <returns></returns>
        public static Boolean GetBooleanDefault(this System.Data.IDataReader dataReader, string fieldName)
        {
            var fieldOrdinal = dataReader.GetOrdinal(fieldName);
            return dataReader.IsDBNull(fieldOrdinal) ? false : dataReader.GetBoolean(fieldOrdinal);
        }

        public static DataUtil ReturnPagingReader(TypeDataBase baseDatos,string select, string join, string where, Dictionary<string, object> parametersSQL, int paginaActual, int cantidadMostrar, string orderBy, ref int totalRecords)
        {
            string sql = join + where;

            using (DataUtil d = new DataUtil(baseDatos, CommandType.Text))
            {
                d.Comando.CommandText = "SELECT COUNT(1) " + sql;
                d.AddParametersDictionary(parametersSQL);
                totalRecords = Convert.ToInt32(d.ExecuteScalar());
            }

            sql = select + sql;

            int filaDesde, filaHasta;

            filaDesde = ((paginaActual - 1) * cantidadMostrar);
            filaHasta = (paginaActual * cantidadMostrar) + 1;

            sql = "SELECT ROW_NUMBER() OVER(ORDER BY " + orderBy + ") AS FILA,FILAS.*  FROM ( " + sql + ") FILAS";

            sql = "SELECT A.*  FROM ( " + sql + ") A WHERE A.FILA > " + filaDesde.ToString() + " AND A.FILA < " + filaHasta.ToString();

            DataUtil data = new DataUtil(baseDatos, CommandType.Text, sql);

            data.AddParametersDictionary(parametersSQL);

            return data;
        }
    }
}